/*
 * Copyright Hundsun Technologies Inc. All Rights Reserved.
 */

package common

import (
	"time"

	"github.com/allegro/bigcache"
	"hundsun.com/hsl/hschain/common/crypto"
	"hundsun.com/hsl/hschain/common/log/logging"
	"hundsun.com/hsl/hschain/common/message"
	"hundsun.com/hsl/hschain/common/types"
	"hundsun.com/hsl/hschain/common/util"
	"hundsun.com/hsl/hschain/common/util/pb"
	consensuscomm "hundsun.com/hsl/hschain/consensus/common"
	dposcomm "hundsun.com/hsl/hschain/consensus/impl/dpos/common"
	pbc "hundsun.com/hsl/hschain/protos/common"
	pbledger "hundsun.com/hsl/hschain/protos/ledger"
)

func initBlockCommon(baseClient *consensuscomm.BaseClient) error {
	var err error
	hashSize, _ := crypto.HashSize()
	genesisPayload := pb.GetGenesisTransactionPayload()
	config := bigcache.Config{
		Shards:             2,
		LifeWindow:         time.Duration(dposcomm.TimeSliceLength) * time.Second * 2,
		CleanWindow:        time.Duration(1) * time.Second,
		MaxEntriesInWindow: 16,
		MaxEntrySize:       hashSize * int(genesisPayload.BlockLimit.MaxTxCount),
		OnRemoveWithReason: func(key string, entry []byte, reason bigcache.RemoveReason) {
			if reason == bigcache.Expired && len(key) > 8 {
				txHashes := util.BytesToTxHashes(entry)
				blkLog := log.WithField(logging.LogFieldBlockHash, key[0:8])

				blkLog.Debug("entry in markedTxLocalCache expired. UndoPreRemoveTransactions[expired] starts...")
				_, err = baseClient.UndoPreRemoveTransactions(txHashes)
				if err != nil {
					blkLog.Warnf("UndoPreRemoveTransaction[expired] failed:%s", err.Error())
				} else {
					blkLog.Info("UndoPreRemoveTransaction[expired] ends")
					//if blkLog.IsTraceEnabled() {
					//	for i := range txHashes {
					//		blkLog.WithField(logging.LogFieldTxHash, txHashes[i].GetShortString()).Tracef("UndoPreRemoveTransaction removed:%d cancelTTL:%d", removed[i*2], removed[i*2+1])
					//	}
					//}
				}
			}
		},
		Logger: log,
	}
	markedTxLocalCache, err := bigcache.NewBigCache(config)
	if err != nil {
		panic(err.Error())
	}
	baseClient.SetMarkedTxLocalCache(markedTxLocalCache)

	lastAcceptedBlock := getLastAcceptedBlock(baseClient)
	log.Infof("last accept block[number=%d,seq=%d] in ledger", lastAcceptedBlock.Header.Number, lastAcceptedBlock.Header.BlockSequence)
	baseClient.SetLastBlockCheckedMessage(pb.FormatBlockCheckedMsg(lastAcceptedBlock))
	lastSeq, err := baseClient.RequestBlockSequence()
	if err != nil {
		panic(err.Error())
	}
	baseClient.SetLastBlockSequence(lastSeq)
	baseClient.SubExecutedBlockFunc(func(block *pbledger.ExecutedBlock) error {
		mqLog := log.WithField(logging.LogFieldMessageTopic, message.TopicExecutedBlock)
		mqLog.Infof("Receive executed block.")
		var blockExecMsg pbc.BlockExecutedMessage
		blockExecMsg.BlockNumber = block.BlockExecResult.BlockNumber
		blockExecMsg.BlockTime = block.Block.Header.BlockTime
		blockExecMsg.StateHash = block.BlockExecResult.StateHash
		blockExecMsg.BlockHash = block.Block.Hash()

		blkLog := mqLog.WithField(logging.LogFieldBlockNumber, blockExecMsg.BlockNumber)
		blkLog.Info("recv")
		baseClient.PutBlockExecutedMessage(&blockExecMsg, blkLog)

		return nil
	})

	return nil
}

func getLastAcceptedBlock(bc *consensuscomm.BaseClient) *pbledger.Block {
	// 说明后续height和blocknumber是一个概念两个相同
	height, err := bc.RequestBlockHeight()
	retryCounter := 0
	for err != nil {
		if err == types.ErrNotFound {
			height = 0
			break
		}
		if retryCounter%1000 == 0 {
			log.Errorf("retried %d times to get block height,err: %s", retryCounter, err.Error())
		}
		height, err = bc.RequestBlockHeight()
		retryCounter++
	}
	if height == 0 {
		return pb.GetGenesisBlock()
	}

	retryCounter = 0
	block, err := bc.RequestBlock(height)
	for err != nil {
		if retryCounter%1000 == 0 {
			log.Errorf("retried %d times to get block[%d],err: %s", retryCounter, height, err.Error())
		}
		block, err = bc.RequestBlock(height)
		retryCounter++
	}
	return block
}
